/**
 * jQuery references to DOM elements
 * @param {UrlObject} url
 * @constructor
 */
function View(url)
{
	"use strict";

	/** @type {View} */
	var _this = this;
	/** @type {Number} */
	var _resizeTimer = 0;
	/** @type {MapMediator} */
	this.map = new MapMediator(_this);
	/** @type {EmbedMediator} */
	this.embed = new EmbedMediator(_this);
	/** @type {ShareMediator} */
	this.share = new ShareMediator(_this, url);
	/** @type {FilmStripMediator} */
	this.strip = new FilmStripMediator(_this);
	/** @type {CaptionMediator} */
	this.caption = new CaptionMediator(_this);
	/** @type {PhotoMediator} */
	this.photo = new PhotoMediator(_this);
	/** @type {MenuMediator} */
	this.menu = new MenuMediator(_this);
	/** @type {ExifMediator} */
	this.exif = new ExifMediator(_this);
	/** @type {SlideShowMediator} */
	this.slideshow = new SlideShowMediator(_this);
	/** @type {QuoteMediator} */
	this.quote = new QuoteMediator(_this);
	/** @type {CommentMediator} */
	this.comment = new CommentMediator(_this);
	/** @type {AboutMediator} */
	this.about = new AboutMediator(_this);
	/** @type {TwitterMediator} */
	this.twitter = new TwitterMediator(_this);
	/** @type {HelpMediator} */
	this.help = new HelpMediator(_this);
	/** @type {ToolbarMediator} */
	this.toolbar = new ToolbarMediator(_this);
	/**
	 * Tools that may only appear exclusively
	 * @type {Array.<BaseMediator>}
	 */
	var _exclusives = null;

	function init()
	{
		_exclusives = [_this.menu, _this.comment, _this.help, _this.about, _this.exif, _this.twitter, _this.embed, _this.share, _this.quote];

		var $flickrLink = $('#flickr-link');

		$flickrLink.hover(
			function() { $(this).find('i').addClass('icon-white'); },
			function() { $(this).find('i').removeClass('icon-white'); }
		);

		$(window).keydown(function(e)
			{
				switch (e.which)
				{
					case 32:									// space bar
					case 39:				                    // forward arrow
						_this.toolbar.highlightNext(true);
						break;
					case 37:                			        // back arrow
						_this.toolbar.highlightPrevious(true);
						break;
					case 27:									// escape
						_this.slideshow.stop();
						break;
					case 77: _this.map.toggle(); break;		    // M
					case 72: _this.hideAll(); break;		    // H
					case 84: _this.showTools(); break;          // T
					case 90: _this.photo.toggleZoom(); break;	// Z
				}
			})
			.resize(function()
			{
				if (_resizeTimer) { window.clearTimeout(_resizeTimer); }
				_resizeTimer = window.setTimeout(handleResize, Enum.duration.windowResize);
			});
	}

	/**
	 * If required, ensure shown view is exclusive on the screen
	 * @param {BaseMediator} shown
	 */
	this.ensureExclusive = function(shown)
	{
		/** @type {Array.<BaseMediator>} */
		var toHide = [];
		/** @type {BaseMediator} */
		var other = null;
		/** @type {Boolean} */
		var isExclusive = false;

		for (var e = 0; e < _exclusives.length; e++)
		{
			other = _exclusives[e];
			if (!isExclusive && other.equals(shown)) { isExclusive = true; } else { toHide.push(other); }
		}
		if (isExclusive)
		{
			// don't bother hiding anything if the shown view is not exclusive
			for (var h = 0; h < toHide.length; h++) { toHide[h].hide(); }
		}
	};

	function handleResize()
	{
		_this.photo.fit();
		_this.strip.fit();
	}

	this.mapClosed = function()
	{
		_this.caption.show();
		_this.menu.fadeIn();
		_this.photo.enableDrag();
		_this.strip.show();
	};

	/**
	 * Hide elements that will conflict with the map overlay
	 */
	this.mapEnlarged = function()
	{
		_this.caption.hide();
		_this.menu.fadeOut();
		_this.quote.hide();
		_this.photo.stopAnimation().disableDrag();
	};

	this.hideAll = function()
	{
		for (var i = 0; i < _exclusives.length; i++) { _exclusives[i].hide();	}
		_this.caption.hide();
		_this.map.hide();
	};

	/**
	 * @return {View}
	 */
	this.showTools = function()
	{
		$('#tools').show();
		return _this;
	};

	/**
	 * @returns {View}
	 */
	this.prepareSlideshow = function()
	{
		_this.caption.hide();
		_this.embed.disable();
		_this.toolbar.disableNavigation().togglePlay(true);
		_this.map.hide().disable();
		return _this;
	};

	/**
	 * @returns {View}
	 */
	this.photoMoving = function()
	{
		_this.photo.stopAnimation();
		//_shell.caption.hide();
		//_shell.map.hide();
		//_shell.quote.hide();
		_this.slideshow.pause();
		return _this;
	};

	/**
	 * @return {View}
	 */
	this.photoMoved = function()
	{
		//_shell.caption.show();
		//_shell.map.show();
		_this.slideshow.restart();
		return _this;
	};

	/**
	 * Image bytes are loading
	 * @return {View}
	 */
	this.photoImageChanging = function()
	{
		return _this;
	};

	/**
	 * Image bytes have loaded
	 * @return {View}
	 */
	this.photoImageChanged = function()
	{
		_this.slideshow.restart();
		_this.map.highlightPhoto();
		return _this;
	};

	/**
	 * Photo object is loading from Flickr
	 * @return {View}
	 */
	this.photoChanging = function()
	{
		if (_this.exif.visible) { _this.exif.busy(); }
		return _this;
	};

	/**
	 * Photo object has loaded from Flickr
	 * @return {View}
	 */
	this.photoChanged = function()
	{
		_this.map.clearHighlights();
		_this.toolbar.updateNavigation();
		_this.caption.update();
		_this.strip.select();
		_this.exif.update();

		if (_this.quote.visible) { _this.quote.show(); }
		if (_this.share.visible) { _this.share.show(); }

		return _this;
	};

	/**
	 * Set object is loading from Flickr
	 * @return {View}
	 */
	this.setChanging = function()
	{
		_this.menu.hide();
		_this.photo.hide();
		_this.slideshow.stop();
		_this.map.hide();
		return _this;
	};

	/**
	 * Set object has loaded from Flickr
	 * @param {Function} [callback] Method called when first photo-of-set image has downloaded
	 * @return {View}
	 */
	this.setChanged = function(callback)
	{
		if (!_this.slideshow.active)
		{
			_this.menu.select(Session.set.id).hide();
			_this.caption.show();
			_this.map.showSet();
			_this.toolbar.enableNavigation();  // prev/next enabled specifically in this.photoChanged()
			_this.embed.updateNavigation().enable();
		}
		_this.photo.show(callback);
		_this.strip.show();

		return _this;
	};

	/**
	 * @param {String} message
	 * @param {String} title
	 * @return {View}
	 */
	this.alert = function(message, title)
	{
		window.alert(title + ': ' + message);
		return _this;
	};

	init();
}

